package client.network;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

import message.Message;
import message.MessageAcceptor;
import message.controller.MessageController;
import message.types.data.AddDataRequest;
import message.types.none.GetAllDatasRequest;
import message.types.none.GetLastDataRequest;
import message.types.stalker.RegisterRequest;
import message.types.string.CreatePrivatePersistentRoomRequest;
import message.types.string.CreateRoomRequest;
import message.types.string.GetRoomFromNameRequest;
import message.types.string.GetUserFromNameRequest;
import message.visitor.ClientMessageVisitor;
import network.e2e.FrameReader;
import network.e2e.FrameWriter;
import network.handler.StalkerNetListener;
import stalker.Stalker;
import client.gui.workbox.WorkBox;
import client.network.interactive.InteractiveRequest;
import data.Data;
import data.content.Content;
import data.content.ContentStalkerJoin;
import data.content.ContentStalkerLeaves;
import data.datas.Datas;

/**
 * Handles requests made from the client to the server
 * <p>
 * I.e., a Post Office!
 */

public class ServiceRequestCenter implements MessageAcceptor {
  
  private final Thread listener_, queue_manager_;
  private final Stalker this_credential_;
  private final MessageController<Void> controller_;
  private WorkBox client_;
  private final List<InteractiveRequest> interactive_requests_;
  private final FrameReader reader_;
  private final FrameWriter writer_;
  
  private void checkRep() {
    assert 
           listener_ != null && queue_manager_ != null &&
           this_credential_ != null && controller_ != null;
  }
  
  public ServiceRequestCenter(Stalker credential, FrameReader reader, FrameWriter writer) {
    assert credential != null && reader != null && writer != null;
    
    reader_ = reader;
    writer_ = writer;
    
    this_credential_ = credential;
    interactive_requests_ = new ArrayList<InteractiveRequest>();
    listener_ = new Thread(new StalkerNetListener(this, reader));
    controller_ = new MessageController<Void>(new ClientMessageVisitor(this, writer));
    queue_manager_ = new Thread(controller_);
    queue_manager_.start();
    listener_.start();
    checkRep();
  }
  
  public void setWorkBox(WorkBox client) {
    assert client != null;
    client_ = client;
  }
  
  public void requestWriteToRoom(Stalker room, Content content) {
    assert room != null && content != null;
    controller_.enqueueMessage(new AddDataRequest(this_credential_, room, content));
  }
  
  public void requestUpdateRoom(Stalker room) {
    assert room != null;
    controller_.enqueueMessage(new GetLastDataRequest(this_credential_, room));
  }
  
  public void requestNewRoom(String roomname) {
    assert roomname != null;
    controller_.enqueueMessage(new CreateRoomRequest(this_credential_, Stalker.ROOM_MANAGER_STALKER, roomname));
  }
  
  public void requestNewPrivatePersistentRoom(String roomname) {
    assert roomname != null;
    controller_.enqueueMessage(new CreatePrivatePersistentRoomRequest(this_credential_, Stalker.ROOM_MANAGER_STALKER, roomname));
  }
  
  public void requestRoomDatas(Stalker room) {
    assert room != null;
    controller_.enqueueMessage(new GetAllDatasRequest(this_credential_, room));
  }
  
  public void requestUserFromName(String name) {
    assert name != null;
    controller_.enqueueMessage(new GetUserFromNameRequest(this_credential_, Stalker.USER_MANAGER, name));
  }
  
  public void requestRoomFromName(String name) {
    assert name != null;
    controller_.enqueueMessage(new GetRoomFromNameRequest(this_credential_, Stalker.ROOM_MANAGER_STALKER, name));
  }
  
  public void requestJoinRoom(Stalker room) {
    assert room != null;
    requestWriteToRoom(room, new ContentStalkerJoin(this_credential_));
  }
  
  public void requestRegisterToRoom(Stalker room) {
    assert room != null;
    controller_.enqueueMessage(new RegisterRequest(this_credential_, room, this_credential_));
  }
  
  public void requestLeaveRoom(Stalker room) {
    assert room != null;
    requestWriteToRoom(room, new ContentStalkerLeaves(this_credential_));
  }
  
  public synchronized void serviceRequest(InteractiveRequest request) {
    assert (request != null);
    request.startRequest(this);
    interactive_requests_.add(request);
  }
  
  public void inviteToRoom(Stalker target, Stalker room) {
    assert target != null && room != null;
    controller_.enqueueMessage(new RegisterRequest(this_credential_, room, target));
  }
  
  public void receiveData(Stalker sender, Data data) {
    client_.receiveData(sender, data);
  }
  
  public void receiveInvitation(Stalker inviter, Stalker room) {
    client_.receiveInvitation(inviter, room);
  }
  
  public void receiveRejectedMessage(Message message) {
    //custom title, warning icon
    JOptionPane.showMessageDialog(new JFrame(),
      "Failed: " + message.toString(),
      "Request rejected",
      JOptionPane.WARNING_MESSAGE);
  }
    
  public void receiveDatas(Stalker sender, Datas datas) {
    List<Data> data_list = datas.retrieveAllDatas();
    for (Data data : data_list) {
      client_.receiveData(sender, data);
    }
  }

  public synchronized void acceptMessage(Message message) {
    assert (message != null);
    int n = interactive_requests_.size();
    for (int i = n-1; i >= 0; --i) {
      if (interactive_requests_.get(i).getReply(this, message)) {
        interactive_requests_.remove(i);
      }
    }
    controller_.enqueueMessage(message);
  }
  
  public Stalker getCredential() {
    return this_credential_;
  }
  
  public void cleanUp() {
    reader_.close();
    queue_manager_.interrupt();
    try {
      queue_manager_.join();
    } catch (InterruptedException e) {
      // interrupted
      return;
    } finally {
      try {
        writer_.flush();
      } catch (IOException e) {
        System.out.println("Flushing fails");
        e.printStackTrace();
      }
      writer_.close();
    }
  }
  
}
